#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include "Comm.hpp"

const static int defaultsockfd = -1;
const int defaultbacklog = 5;

class Socket
{
public:
    Socket()
    {
    }

    ~Socket()
    {
    }

    // 1.创建socket
    virtual bool CreateSocketOrDie() = 0;
    // 2.绑定(服务端)
    virtual bool BindSocketOrDie(uint16_t &port) = 0;
    // 3.设置监听
    virtual bool ListenSocketOrDie(int backlog) = 0;
    // 4.获取连接（服务端）
    virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) = 0;
    // 5.发送连接请求（客户端）
    virtual bool ConnectSocket(std::string &ip, uint16_t &port) = 0;
    //6.连接完毕获取新sockfd
    virtual void SetSockfd(int sockfd) = 0;
    virtual int GetSockfd() = 0;
    virtual void CloseSocket() = 0;
    //7.发送消息
    virtual void Send(std::string &send_str) = 0;
    virtual void Recv(std::string *inbuffer, int size) = 0;

public:
    // 服务端建立listen套接字
    void BulidListenSocketMethod(uint16_t port, int backlog = defaultbacklog)
    {
        CreateSocketOrDie();
        BindSocketOrDie(port);
        ListenSocketOrDie(backlog);
    }

    // 客户端获取连接
    bool BuildConnectionSocketMethod(std::string serverip, uint16_t serverport)
    {
        CreateSocketOrDie();
        return ConnectSocket(serverip, serverport);
    }

    //建立连接后正常通信
    void BuildNormalSocketMethod(int sockfd)
    {
        SetSockfd(sockfd);
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd)
    {
    }

    ~TcpSocket()
    {
    }

    bool CreateSocketOrDie() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd != 0)
        {
            // TODO
            return false;
        }
        return true;
    }

    bool BindSocketOrDie(uint16_t &serverport) override
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        //inet_pton，
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = ::htons(serverport);
        int n = ::bind(_sockfd, CONV(&server), sizeof(server));
        if (n != 0)
        {
            //TODO
            return false;
        }
        return true;
    }

    bool ListenSocketOrDie(int backlog) override
    {
        int n = ::listen(_sockfd, backlog);
        if (n != 0)
        {
            //TODO
            return false;
        }
        return true;
    }

    Socket *AcceptConnection(std::string *peerip, uint16_t *port) override
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sockfd = ::accept(_sockfd, CONV(&peer), &len);
        if (sockfd < 0)
            return nullptr;
        //::inet_ntop(AF_INET, &peer.sin_addr, peerip, sizeof(*peerip));
        *peerip = inet_ntoa(peer.sin_addr);
        *port = ntohs(peer.sin_port);
        Socket *s = new TcpSocket(sockfd);
        return s;
    }

    bool ConnectSocket(std::string &ip, uint16_t &port) override
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        ::inet_pton(AF_INET, ip.c_str(), &server.sin_addr);
        server.sin_port = ::htons(port);
        int n =  ::connect(_sockfd, CONV(&server), sizeof(server));
        if (n < 0)
        {
            //TODO
            return false;
        }
        return true;
    }

    void SetSockfd(int sockfd) override
    {
        _sockfd = sockfd;
    }

    int GetSockfd() override
    {
        return _sockfd;
    }

    void CloseSocket() override
    {
        if (_sockfd > defaultsockfd)
            close(_sockfd);
    }

    void Send(std::string &send_str) override
    {
        ssize_t n = ::send(_sockfd, send_str.c_str(), send_str.size(), 0);
        if (n < 0)
        {
            //TODO
            
        }
    }

    void Recv(std::string *buffer, int size) override
    {
        char inbuffer[size];
        ssize_t n = ::recv(_sockfd, inbuffer, size, 0);
        if (n < 0)
        {
            //TODO
        }
        inbuffer[n] = 0;
        *buffer += inbuffer;
    }
private:
    int _sockfd;
};